﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;

namespace CommonLibary
{
    /// <summary>
    /// 安全相关通用功能
    /// </summary>
    public static class SecurityHelper
    {
        //private static string _key = "BNDJT_DS";
        /// <summary>
        /// 加密
        /// </summary>
        /// <param name="pToEncrypt">待加密字符串</param>
        /// <param name="sKey">加密密钥</param>
        /// <returns>加密后的字符串</returns>
        public static string Encrypt(this string pToEncrypt, string sKey)
        {
            int requiredKeyLength = 8;
            if (string.IsNullOrEmpty(sKey))
                return string.Empty;
            if (sKey.Length > requiredKeyLength)
                sKey = sKey.Substring(0, requiredKeyLength);
            DESCryptoServiceProvider des = new DESCryptoServiceProvider();
            byte[] inputByteArray = Encoding.Default.GetBytes(pToEncrypt);
            des.Key = ASCIIEncoding.ASCII.GetBytes(sKey);
            des.IV = ASCIIEncoding.ASCII.GetBytes(sKey);
            MemoryStream ms = new MemoryStream();
            CryptoStream cs = new CryptoStream(ms, des.CreateEncryptor(), CryptoStreamMode.Write);
            cs.Write(inputByteArray, 0, inputByteArray.Length);
            cs.FlushFinalBlock();
            StringBuilder ret = new StringBuilder();
            foreach (byte b in ms.ToArray())
            {
                ret.AppendFormat("{0:X2}", b);
            }
            ret.ToString();
            return ret.ToString();
        }

        //public static string Encrypt(this string pToEncrypt)
        //{
        //    return Encrypt(pToEncrypt, _key);
        //}


        public static String SearchEncrypt(String strText, string key)
        {
            Byte[] byKey = { };
            Byte[] IV = { 0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF };
            try
            {
                byKey = System.Text.Encoding.UTF8.GetBytes(key.Substring(0, 8));
                DESCryptoServiceProvider des = new DESCryptoServiceProvider();
                Byte[] inputByteArray = Encoding.UTF8.GetBytes(strText);
                MemoryStream ms = new MemoryStream();
                CryptoStream cs = new CryptoStream(ms, des.CreateEncryptor(byKey, IV), CryptoStreamMode.Write);
                cs.Write(inputByteArray, 0, inputByteArray.Length);
                cs.FlushFinalBlock();
                return System.Web.HttpUtility.UrlEncode(Convert.ToBase64String(ms.ToArray()));
            }

            catch (Exception ex)
            {
                return string.Empty;
            }
        }

        /// <summary>
        /// 解密
        /// </summary>
        /// <param name="pToDecrypt">待解密字符串</param>
        /// <param name="sKey">解密密钥</param>
        /// <returns>解密后的字符串</returns>
        public static string Decrypt(this string pToDecrypt, string sKey)
        {
            int requiredKeyLength = 8;
            if (string.IsNullOrEmpty(sKey))
                return string.Empty;
            if (sKey.Length > requiredKeyLength)
                sKey = sKey.Substring(0, requiredKeyLength);
            using (DESCryptoServiceProvider des = new DESCryptoServiceProvider())
            {

                byte[] inputByteArray = new byte[pToDecrypt.Length / 2];
                for (int x = 0; x < pToDecrypt.Length / 2; x++)
                {
                    int i = (Convert.ToInt32(pToDecrypt.Substring(x * 2, 2), 16));
                    inputByteArray[x] = (byte)i;
                }

                des.Key = ASCIIEncoding.ASCII.GetBytes(sKey);
                des.IV = ASCIIEncoding.ASCII.GetBytes(sKey);
                using (MemoryStream ms = new MemoryStream())
                {
                    using (var t = des.CreateDecryptor())
                    {
                        using (CryptoStream cs = new CryptoStream(ms, t, CryptoStreamMode.Write))
                        {
                            cs.Write(inputByteArray, 0, inputByteArray.Length);
                            cs.FlushFinalBlock();
                            StringBuilder ret = new StringBuilder();
                            return System.Text.Encoding.Default.GetString(ms.ToArray());
                        }
                    }
                }
            }
        }

        //public static string Decrypt(this string pToDecrypt)
        //{
        //    return Decrypt(pToDecrypt, _key);
        //}

        /// <summary>
        /// base64加密
        /// </summary>
        /// <param name="pToEncrypt">待加密字符串</param>
        /// <param name="encoding">编码</param>
        /// <returns></returns>
        public static string EncryptBase64(this string pToEncrypt, string encoding, bool isDebug)
        {
            string encode = "";
            byte[] bytes = Encoding.GetEncoding(encoding).GetBytes(pToEncrypt);
            try
            {
                encode = Convert.ToBase64String(bytes);
            }
            catch (Exception ex)
            {
                if (isDebug)
                {
                    encode = ex.ToString();
                }
                else
                {
                    encode = pToEncrypt;
                }
            }
            return encode;
        }
        public static string EncryptBase64(this string pToEncrypt)
        {
            return pToEncrypt.EncryptBase64("utf-8", false);
        }

        /// <summary>
        /// base64解密
        /// </summary>
        /// <param name="pToEncrypt">待解密字符串</param>
        /// <param name="encoding">编码</param>
        /// <returns></returns>
        public static string DecryptBase64(this string pToEncrypt, string encoding, bool isDebug)
        {
            string decode = "";
            byte[] bytes = Convert.FromBase64String(pToEncrypt);
            try
            {
                decode = Encoding.GetEncoding(encoding).GetString(bytes);
            }
            catch (Exception ex)
            {
                if (isDebug)
                {
                    decode = ex.ToString();
                }
                else
                {
                    decode = pToEncrypt;
                }
            }
            return decode;
        }
        public static string DecryptBase64(this string pToEncrypt)
        {
            return pToEncrypt.DecryptBase64("utf-8", false);
        }

        public static string MD5(this string sDataIn)
        {
            return sDataIn.MD5(string.Empty);
        }

        public static string MD5(this string toEncrypt, string key)
        {
            System.Security.Cryptography.MD5CryptoServiceProvider md5 = new System.Security.Cryptography.MD5CryptoServiceProvider();
            byte[] byt, bytHash;
            byt = System.Text.Encoding.UTF8.GetBytes(key + toEncrypt);
            bytHash = md5.ComputeHash(byt);
            md5.Clear();
            string result = "";
            for (int i = 0; i < bytHash.Length; i++)
            {
                result += bytHash[i].ToString("x").PadLeft(2, '0');
            }
            return result;
        }

        /// <summary>
        /// 有密码的AES加密 
        /// </summary>
        /// <param name="text">加密字符</param>
        /// <param name="password">加密的密码</param>
        /// <param name="iv">密钥</param>
        /// <returns></returns>
        public static string EncryptAES(string toEncrypt, string key)
        {
            byte[] keyArray = UTF8Encoding.UTF8.GetBytes(key);
            byte[] toEncryptArray = UTF8Encoding.UTF8.GetBytes(toEncrypt);

            RijndaelManaged rDel = new RijndaelManaged();
            rDel.Key = keyArray;
            rDel.Mode = CipherMode.ECB;
            rDel.Padding = PaddingMode.PKCS7;

            ICryptoTransform cTransform = rDel.CreateEncryptor();
            byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);

            return Convert.ToBase64String(resultArray, 0, resultArray.Length);
        }

        /// <summary>
        /// AES解密
        /// </summary>
        /// <param name="text"></param>
        /// <param name="password"></param>
        /// <param name="iv"></param>
        /// <returns></returns>
        public static string DecryptAES(string toDecrypt, string key)
        {
            byte[] keyArray = UTF8Encoding.UTF8.GetBytes(key);
            byte[] toEncryptArray = Convert.FromBase64String(toDecrypt);

            RijndaelManaged rDel = new RijndaelManaged();
            rDel.Key = keyArray;
            rDel.Mode = CipherMode.ECB;
            rDel.Padding = PaddingMode.PKCS7;

            ICryptoTransform cTransform = rDel.CreateDecryptor();
            byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);

            return UTF8Encoding.UTF8.GetString(resultArray);
        }

        /// <summary>
        /// 随机生成16位AESkey
        /// </summary>
        /// <returns></returns>
        public static string GenerateAESKey()
        {
            string str = string.Empty;

            Random rnd1 = new Random(Guid.NewGuid().GetHashCode());
            int r = rnd1.Next(10, 100);

            long num2 = DateTime.Now.Ticks + r;

            Random random = new Random(((int)(((ulong)num2) & 0xffffffffL)) | ((int)(num2 >> r)));
            for (int i = 0; i < 16; i++)
            {
                char ch;
                int num = random.Next();
                if ((num % 2) == 0)
                {
                    ch = (char)(0x30 + ((ushort)(num % 10)));
                }
                else
                {
                    ch = (char)(0x41 + ((ushort)(num % 0x1a)));
                }
                str = str + ch.ToString();
            }
            return str;
        }

        #region HmacMD5
        /// <summary>
        /// 
        /// </summary>
        /// <param name="toEncrypt"></param>
        /// <param name="key">长度小于等于63，否则报错,常用的是取60</param>
        /// <returns></returns>
        public static string HmacMD5(this string toEncrypt, string key)
        {
            byte[] k_ipad = new byte[64];
            byte[] k_opad = new byte[64];
            byte[] keyb;
            byte[] Value;
            keyb = Encoding.UTF8.GetBytes(key);
            Value = Encoding.UTF8.GetBytes(toEncrypt);

            for (int i = keyb.Length; i < 64; i++)
                k_ipad[i] = 54;

            for (int i = keyb.Length; i < 64; i++)
                k_opad[i] = 92;

            for (int i = 0; i < keyb.Length; i++)
            {
                k_ipad[i] = (byte)(keyb[i] ^ 0x36);
                k_opad[i] = (byte)(keyb[i] ^ 0x5c);
            }

            HmacMD5 md = new HmacMD5();

            md.update(k_ipad, (uint)k_ipad.Length);
            md.update(Value, (uint)Value.Length);
            byte[] dg = md.finalize();
            md.init();
            md.update(k_opad, (uint)k_opad.Length);
            md.update(dg, 16);
            dg = md.finalize();

            return toHex(dg);
        }

        private static string toHex(byte[] input)
        {
            if (input == null)
                return null;

            StringBuilder output = new StringBuilder(input.Length * 2);

            for (int i = 0; i < input.Length; i++)
            {
                int current = input[i] & 0xff;
                if (current < 16)
                    output.Append("0");
                output.Append(current.ToString("x"));
            }

            return output.ToString();
        }
        #endregion

        /// <summary>
        /// 对字符串进行SHA1加密
        /// </summary>
        /// <param name="Source_String">需要加密的字符串</param>
        /// <returns>密文</returns>
        public static string SHA1(string Source_String)
        {
            byte[] StrRes = Encoding.Default.GetBytes(Source_String);
            HashAlgorithm iSHA = new SHA1CryptoServiceProvider();
            StrRes = iSHA.ComputeHash(StrRes);
            StringBuilder EnText = new StringBuilder();
            foreach (byte iByte in StrRes)
            {
                EnText.AppendFormat("{0:x2}", iByte);
            }
            return EnText.ToString();
        }

    }

    public static class Base64UrlEncoder
    {
        private static char base64PadCharacter = '=';
        private static string doubleBase64PadCharacter = "==";
        private static string threeBase64PadCharacter = "===";
        private static char base64Character62 = '+';
        private static char base64Character63 = '/';
        private static char base64UrlCharacter62 = '-';
        private static char _base64UrlCharacter63 = '_';

        /// <summary>
        /// The following functions perform base64url encoding which differs from regular base64 encoding as follows
        /// * padding is skipped so the pad character '=' doesn't have to be percent encoded
        /// * the 62nd and 63rd regular base64 encoding characters ('+' and '/') are replace with ('-' and '_')
        /// The changes make the encoding alphabet file and URL safe.
        /// </summary>
        /// <param name="arg">string to encode.</param>
        /// <returns>Base64Url encoding of the UTF8 bytes.</returns>
        public static string Base64UrlEncode(this string arg)
        {
            if (null == arg)
                throw new ArgumentNullException("arg");

            return Encode(Encoding.UTF8.GetBytes(arg));
        }

        /// <summary>
        /// Converts a subset of an array of 8-bit unsigned integers to its equivalent string representation that is encoded with base-64-url digits. Parameters specify
        /// the subset as an offset in the input array, and the number of elements in the array to convert.
        /// </summary>
        /// <param name="inArray">An array of 8-bit unsigned integers.</param>
        /// <param name="length">An offset in inArray.</param>
        /// <param name="offset">The number of elements of inArray to convert.</param>
        /// <returns>The string representation in base 64 url encodingof length elements of inArray, starting at position offset.</returns>
        /// <exception cref="ArgumentNullException">'inArray' is null.</exception>
        /// <exception cref="ArgumentOutOfRangeException">offset or length is negative OR offset plus length is greater than the length of inArray.</exception>
        public static string Encode(byte[] inArray, int offset, int length)
        {
            if (inArray == null)
                throw new ArgumentNullException("inArray");

            string s = Convert.ToBase64String(inArray, offset, length);
            s = s.Split(base64PadCharacter)[0]; // Remove any trailing padding
            s = s.Replace(base64Character62, base64UrlCharacter62);  // 62nd char of encoding
            s = s.Replace(base64Character63, _base64UrlCharacter63);  // 63rd char of encoding
            return s;
        }

        /// <summary>
        /// Converts a subset of an array of 8-bit unsigned integers to its equivalent string representation that is encoded with base-64-url digits. Parameters specify
        /// the subset as an offset in the input array, and the number of elements in the array to convert.
        /// </summary>
        /// <param name="inArray">An array of 8-bit unsigned integers.</param>
        /// <returns>The string representation in base 64 url encodingof length elements of inArray, starting at position offset.</returns>
        /// <exception cref="ArgumentNullException">'inArray' is null.</exception>
        /// <exception cref="ArgumentOutOfRangeException">offset or length is negative OR offset plus length is greater than the length of inArray.</exception>
        public static string Encode(byte[] inArray)
        {
            if (inArray == null)
                throw new ArgumentNullException("inArray");

            string s = Convert.ToBase64String(inArray, 0, inArray.Length);
            s = s.Split(base64PadCharacter)[0]; // Remove any trailing padding
            s = s.Replace(base64Character62, base64UrlCharacter62);  // 62nd char of encoding
            s = s.Replace(base64Character63, _base64UrlCharacter63);  // 63rd char of encoding

            return s;
        }

        /// <summary>
        ///  Converts the specified string, which encodes binary data as base-64-url digits, to an equivalent 8-bit unsigned integer array.</summary>
        /// <param name="str">base64Url encoded string.</param>
        /// <returns>UTF8 bytes.</returns>
        public static byte[] DecodeBytes(string str)
        {
            if (null == str)
            {
                throw new ArgumentNullException("str");
            }

            // 62nd char of encoding
            str = str.Replace(base64UrlCharacter62, base64Character62);

            // 63rd char of encoding
            str = str.Replace(_base64UrlCharacter63, base64Character63);

            // check for padding
            switch (str.Length % 4)
            {
                case 0:
                case 1:
                    // No pad chars in this case
                    break;
                //case 1:
                //    // Three pad chars
                //    str += threeBase64PadCharacter;
                //    break;
                case 2:
                    // Two pad chars
                    str += doubleBase64PadCharacter;
                    break;
                case 3:
                    // One pad char
                    str += base64PadCharacter;
                    break;
                default:
                    throw new ArgumentNullException("str.Length % 4");
            }

            return Convert.FromBase64String(str);
        }

        /// <summary>
        /// Decodes the string from Base64UrlEncoded to UTF8.
        /// </summary>
        /// <param name="arg">string to decode.</param>
        /// <returns>UTF8 string.</returns>
        public static string Base64UrlDecode(this string arg)
        {
            return Encoding.UTF8.GetString(DecodeBytes(arg));
        }
    }

    /// <summary>
    /// HmacMD5加密
    /// </summary>
    public class HmacMD5
    {
        private uint[] count;
        private uint[] state;
        private byte[] buffer;
        private byte[] Digest;

        public HmacMD5()
        {
            count = new uint[2];
            state = new uint[4];
            buffer = new byte[64];
            Digest = new byte[16];
            init();
        }

        public void init()
        {
            count[0] = 0;
            count[1] = 0;
            state[0] = 0x67452301;
            state[1] = 0xefcdab89;
            state[2] = 0x98badcfe;
            state[3] = 0x10325476;
        }

        public void update(byte[] data, uint length)
        {
            uint left = length;
            uint offset = (count[0] >> 3) & 0x3F;
            uint bit_length = (uint)(length << 3);
            uint index = 0;

            if (length <= 0)
                return;

            count[0] += bit_length;
            count[1] += (length >> 29);
            if (count[0] < bit_length)
                count[1]++;

            if (offset > 0)
            {
                uint space = 64 - offset;
                uint copy = (offset + length > 64 ? 64 - offset : length);
                Buffer.BlockCopy(data, 0, buffer, (int)offset, (int)copy);

                if (offset + copy < 64)
                    return;

                transform(buffer);
                index += copy;
                left -= copy;
            }

            for (; left >= 64; index += 64, left -= 64)
            {
                Buffer.BlockCopy(data, (int)index, buffer, 0, 64);
                transform(buffer);
            }

            if (left > 0)
                Buffer.BlockCopy(data, (int)index, buffer, 0, (int)left);

        }

        private static byte[] pad = new byte[64] {
                                                     0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                                                     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                                                     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                                                     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
        public byte[] finalize()
        {
            byte[] bits = new byte[8];
            encode(ref bits, count, 8);
            uint index = (uint)((count[0] >> 3) & 0x3f);
            uint padLen = (index < 56) ? (56 - index) : (120 - index);
            update(pad, padLen);
            update(bits, 8);
            encode(ref Digest, state, 16);

            for (int i = 0; i < 64; i++)
                buffer[i] = 0;

            return Digest;
        }

        public string md5String()
        {
            string s = "";

            for (int i = 0; i < Digest.Length; i++)
                s += Digest[i].ToString("x2");

            return s;
        }

        #region Constants for MD5Transform routine.

        private const uint S11 = 7;
        private const uint S12 = 12;
        private const uint S13 = 17;
        private const uint S14 = 22;
        private const uint S21 = 5;
        private const uint S22 = 9;
        private const uint S23 = 14;
        private const uint S24 = 20;
        private const uint S31 = 4;
        private const uint S32 = 11;
        private const uint S33 = 16;
        private const uint S34 = 23;
        private const uint S41 = 6;
        private const uint S42 = 10;
        private const uint S43 = 15;
        private const uint S44 = 21;
        #endregion

        private void transform(byte[] data)
        {
            uint a = state[0];
            uint b = state[1];
            uint c = state[2];
            uint d = state[3];
            uint[] x = new uint[16];

            decode(ref x, data, 64);

            // Round 1
            FF(ref a, b, c, d, x[0], S11, 0xd76aa478); /* 1 */
            FF(ref d, a, b, c, x[1], S12, 0xe8c7b756); /* 2 */
            FF(ref c, d, a, b, x[2], S13, 0x242070db); /* 3 */
            FF(ref b, c, d, a, x[3], S14, 0xc1bdceee); /* 4 */
            FF(ref a, b, c, d, x[4], S11, 0xf57c0faf); /* 5 */
            FF(ref d, a, b, c, x[5], S12, 0x4787c62a); /* 6 */
            FF(ref c, d, a, b, x[6], S13, 0xa8304613); /* 7 */
            FF(ref b, c, d, a, x[7], S14, 0xfd469501); /* 8 */
            FF(ref a, b, c, d, x[8], S11, 0x698098d8); /* 9 */
            FF(ref d, a, b, c, x[9], S12, 0x8b44f7af); /* 10 */
            FF(ref c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */
            FF(ref b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */
            FF(ref a, b, c, d, x[12], S11, 0x6b901122); /* 13 */
            FF(ref d, a, b, c, x[13], S12, 0xfd987193); /* 14 */
            FF(ref c, d, a, b, x[14], S13, 0xa679438e); /* 15 */
            FF(ref b, c, d, a, x[15], S14, 0x49b40821); /* 16 */

            // Round 2 
            GG(ref a, b, c, d, x[1], S21, 0xf61e2562); /* 17 */
            GG(ref d, a, b, c, x[6], S22, 0xc040b340); /* 18 */
            GG(ref c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */
            GG(ref b, c, d, a, x[0], S24, 0xe9b6c7aa); /* 20 */
            GG(ref a, b, c, d, x[5], S21, 0xd62f105d); /* 21 */
            GG(ref d, a, b, c, x[10], S22, 0x2441453); /* 22 */
            GG(ref c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */
            GG(ref b, c, d, a, x[4], S24, 0xe7d3fbc8); /* 24 */
            GG(ref a, b, c, d, x[9], S21, 0x21e1cde6); /* 25 */
            GG(ref d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */
            GG(ref c, d, a, b, x[3], S23, 0xf4d50d87); /* 27 */
            GG(ref b, c, d, a, x[8], S24, 0x455a14ed); /* 28 */
            GG(ref a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */
            GG(ref d, a, b, c, x[2], S22, 0xfcefa3f8); /* 30 */
            GG(ref c, d, a, b, x[7], S23, 0x676f02d9); /* 31 */
            GG(ref b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */

            // Round 3
            HH(ref a, b, c, d, x[5], S31, 0xfffa3942); /* 33 */
            HH(ref d, a, b, c, x[8], S32, 0x8771f681); /* 34 */
            HH(ref c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */
            HH(ref b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */
            HH(ref a, b, c, d, x[1], S31, 0xa4beea44); /* 37 */
            HH(ref d, a, b, c, x[4], S32, 0x4bdecfa9); /* 38 */
            HH(ref c, d, a, b, x[7], S33, 0xf6bb4b60); /* 39 */
            HH(ref b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */
            HH(ref a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */
            HH(ref d, a, b, c, x[0], S32, 0xeaa127fa); /* 42 */
            HH(ref c, d, a, b, x[3], S33, 0xd4ef3085); /* 43 */
            HH(ref b, c, d, a, x[6], S34, 0x4881d05); /* 44 */
            HH(ref a, b, c, d, x[9], S31, 0xd9d4d039); /* 45 */
            HH(ref d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */
            HH(ref c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */
            HH(ref b, c, d, a, x[2], S34, 0xc4ac5665); /* 48 */

            // Round 4
            II(ref a, b, c, d, x[0], S41, 0xf4292244); /* 49 */
            II(ref d, a, b, c, x[7], S42, 0x432aff97); /* 50 */
            II(ref c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */
            II(ref b, c, d, a, x[5], S44, 0xfc93a039); /* 52 */
            II(ref a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */
            II(ref d, a, b, c, x[3], S42, 0x8f0ccc92); /* 54 */
            II(ref c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */
            II(ref b, c, d, a, x[1], S44, 0x85845dd1); /* 56 */
            II(ref a, b, c, d, x[8], S41, 0x6fa87e4f); /* 57 */
            II(ref d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */
            II(ref c, d, a, b, x[6], S43, 0xa3014314); /* 59 */
            II(ref b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */
            II(ref a, b, c, d, x[4], S41, 0xf7537e82); /* 61 */
            II(ref d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */
            II(ref c, d, a, b, x[2], S43, 0x2ad7d2bb); /* 63 */
            II(ref b, c, d, a, x[9], S44, 0xeb86d391); /* 64 */

            state[0] += a;
            state[1] += b;
            state[2] += c;
            state[3] += d;

            for (int i = 0; i < 16; i++)
                x[i] = 0;
        }

        #region encode - decode
        private void encode(ref byte[] output, uint[] input, uint len)
        {
            uint i, j;
            if (System.BitConverter.IsLittleEndian)
            {
                for (i = 0, j = 0; j < len; i++, j += 4)
                {
                    output[j] = (byte)(input[i] & 0xff);
                    output[j + 1] = (byte)((input[i] >> 8) & 0xff);
                    output[j + 2] = (byte)((input[i] >> 16) & 0xff);
                    output[j + 3] = (byte)((input[i] >> 24) & 0xff);
                }
            }
            else
            {
                for (i = 0, j = 0; j < len; i++, j += 4)
                {
                    output[j + 3] = (byte)(input[i] & 0xff);
                    output[j + 2] = (byte)((input[i] >> 8) & 0xff);
                    output[j + 1] = (byte)((input[i] >> 16) & 0xff);
                    output[j] = (byte)((input[i] >> 24) & 0xff);
                }
            }
        }

        private void decode(ref uint[] output, byte[] input, uint len)
        {
            uint i, j;
            if (System.BitConverter.IsLittleEndian)
            {
                for (i = 0, j = 0; j < len; i++, j += 4)
                    output[i] = ((uint)input[j]) | (((uint)input[j + 1]) << 8) |
                        (((uint)input[j + 2]) << 16) | (((uint)input[j + 3]) << 24);
            }
            else
            {
                for (i = 0, j = 0; j < len; i++, j += 4)
                    output[i] = ((uint)input[j + 3]) | (((uint)input[j + 2]) << 8) |
                        (((uint)input[j + 1]) << 16) | (((uint)input[j]) << 24);
            }
        }
        #endregion

        private uint rotate_left(uint x, uint n)
        {
            return (x << (int)n) | (x >> (int)(32 - n));
        }

        #region F, G, H and I are basic MD5 functions.
        private uint F(uint x, uint y, uint z)
        {
            return (x & y) | (~x & z);
        }

        private uint G(uint x, uint y, uint z)
        {
            return (x & z) | (y & ~z);
        }

        private uint H(uint x, uint y, uint z)
        {
            return x ^ y ^ z;
        }

        private uint I(uint x, uint y, uint z)
        {
            return y ^ (x | ~z);
        }
        #endregion

        #region  FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.
        private void FF(ref uint a, uint b, uint c, uint d, uint x, uint s, uint ac)
        {
            a += F(b, c, d) + x + ac;
            a = rotate_left(a, s) + b;
        }

        private void GG(ref uint a, uint b, uint c, uint d, uint x, uint s, uint ac)
        {
            a += G(b, c, d) + x + ac;
            a = rotate_left(a, s) + b;
        }

        private void HH(ref uint a, uint b, uint c, uint d, uint x, uint s, uint ac)
        {
            a += H(b, c, d) + x + ac;
            a = rotate_left(a, s) + b;
        }

        private void II(ref uint a, uint b, uint c, uint d, uint x, uint s, uint ac)
        {
            a += I(b, c, d) + x + ac;
            a = rotate_left(a, s) + b;
        }
        #endregion
    }
}
